#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <assert.h>

#include "Object.h"
#include "ObjectDefaults.h"
#include "types/DataType.h"
#include "types/DataTypeFactory.h"
#include "RawDataInfo.h"

std::map<std::string, RawDataInfo * >  Object::rawDataInfoMap;


Object::Object( std::ifstream &infile, ObjectDefaults *objectDefaults, LeadIn* li )
    : infile( infile ), objectDefaults( objectDefaults ), _ptrLeadIn( li ), rawData( 0 )
{
    readPath();
//    std::cout << "         cur object: " << getPath() << std::endl;

    objectDefaults->initializeObject( this );
    readRawDataInfo();
    readPropertyCount();
    for ( unsigned int i = 0; i < propertyCount; ++i )
    {
        Property property = readProperty();
    }
}

Object::Property Object::readProperty()
{
    unsigned int strlen;
    infile.read(( char* )&strlen, 4 );
    char buffer[strlen];
    infile.read( buffer, strlen );
    std::string name( buffer, strlen );
    unsigned int itype;
    infile.read(( char* )&itype, 4 );
    const DataType *dataType = DataTypeFactory::instanceFromIndex( itype );
    DataValue *value = dataType->readValue( infile );
    return Property( name, value );
}

void Object::readPath()
{
    unsigned int strlen = 0;
    infile.read(( char* )&strlen, 4 );
    char buffer[strlen];
    infile.read( buffer, strlen );
    path = std::string( buffer, strlen );
}

void Object::readRawDataInfo()
{
    std::string objPath = getPath();
    infile.read(( char* )&rawDataIndex, 4 );
    flagHasRawData = ( rawDataIndex != 4294967295 );
    if( flagHasRawData && rawDataIndex == 0 )
    {
        auto it = Object::rawDataInfoMap.find( objPath );
        if( it == Object::rawDataInfoMap.end() )
        {
            std::cerr << "Failed to get element in map." << objPath << std::endl;
            return;
        }

        RawDataInfo* rdi = getRawDataInfo( objPath );
        assert( rdi != nullptr );
        dimension = rdi->dimension;
        dataType = rdi->dataType;
        nvalue = rdi->nvalue;
        nbytes = rdi->nbytes;
    }
    else if ( flagHasRawData && rawDataIndex > 0 )
    {
        unsigned int itype;
        infile.read(( char* )&itype, 4 );
        dataType = DataTypeFactory::instanceFromIndex( itype );
        infile.read(( char* )&dimension, 4 );
        infile.read(( char* )&nvalue, 8 );
        if ( itype == 32 )
        { infile.read(( char* )&nbytes, 8 ); }

        RawDataInfo* rdi = nullptr;
        auto it = Object::rawDataInfoMap.find( objPath );
        if( it != Object::rawDataInfoMap.end() )
        {
            rdi = getRawDataInfo( objPath );
        }
        else
        { rdi = new RawDataInfo; }

        if( rdi == nullptr )
        { rdi = new RawDataInfo; }
        rdi->dataType = DataTypeFactory::instanceFromIndex( itype );
        rdi->indexLen = rawDataIndex;
        rdi->dimension = dimension;
        rdi->nvalue = nvalue;
        rdi->nbytes = nbytes;

        if( it == Object::rawDataInfoMap.end()
                || getRawDataInfo( objPath ) == nullptr )
        { addEntry( objPath, rdi ); }
    }
}

void Object::readPropertyCount()
{
    infile.read(( char* )&propertyCount, 4 );
}

void Object::readRawData()
{
//    if( dataType->getType() == 68 )
//    {
//        std::cout << "test: timestamp read RawData" << std::endl;
//    }

//    int rawDataLen = _ptrLeadIn->getNxtSegOffset() - _ptrLeadIn->getDataOffset();
//    int segDataLen = dataType->getDatTypLen() * dimension * nvalue;
//    int chunkSize = rawDataLen / segDataLen;
//    if( rawDataLen % segDataLen != 0 )
//    { chunkSize ++; }
//    for( int i = 0; i < chunkSize; i++ )
//    {
    rawData = dataType->readArray( infile, nvalue, nbytes );
//    }

    std::cout << "         readRawData   "
              << getPath() << "    valueNum:" << nvalue
              << "  cur file pos: " << infile.tellg()
              << std::endl;
}

bool Object::hasRawData() const
{
    return flagHasRawData;
}

const std::string& Object::getPath() const
{
    return path;
}

const DataType *Object::getDataType() const
{
    return dataType;
}

const DataArray *Object::getRawData() const
{
    return rawData;
}

void Object::addEntry( const std::string &key, RawDataInfo * value )
{
    Object::rawDataInfoMap[key] = value;
}

RawDataInfo *Object::getRawDataInfo( const std::string &key )
{
    return Object::rawDataInfoMap[key];
}


